package com.nlina.qiniu_bigwork.controller;

import com.nlina.qiniu_bigwork.model.FileDto;
import com.nlina.qiniu_bigwork.service.QiniuService;
import com.qiniu.common.QiniuException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.client.RestTemplate;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpEntity;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ByteArrayResource;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import org.springframework.util.StringUtils;

/**
 * 七牛云盘主控制器
 * 处理与七牛云存储相关的所有操作，包括文件上传、下载、列表显示和删除等功能
 * 提供Web页面和REST API两种访问方式
 */
@Controller
@RequestMapping("/")
public class QiniuController {
    
    private static final Logger log = LoggerFactory.getLogger(QiniuController.class);
    
    /**
     * 一次最多显示的文件数量限制，防止请求过多数据导致性能问题
     */
    private static final int MAX_FILES = 1000;

    /**
     * 七牛云服务，封装了对七牛云API的所有操作
     */
    @Autowired
    private QiniuService qiniuService;

    /**
     * 首页，显示文件列表
     * 
     * @param model 视图模型
     * @param prefix 可选的文件名前缀过滤条件
     * @return 首页视图名称
     */
    @GetMapping
    public String index(Model model, @RequestParam(value = "prefix", required = false, defaultValue = "") String prefix) {
        try {
            log.info("访问首页，前缀: {}", prefix);
            
            // 验证配置并获取文件列表
            if (!qiniuService.validateConfig()) {
                addError(model, "七牛云配置无效，请检查配置信息");
                return "index";
            }
            
            // 获取文件列表
            List<FileDto> files = new ArrayList<>();
            try {
                files = qiniuService.listFiles(prefix, MAX_FILES);
                
                if (files.isEmpty()) {
                    // 如果指定前缀没有找到文件，尝试显示所有文件
                    if (prefix != null && !prefix.isEmpty()) {
                        model.addAttribute("warning", "未找到符合前缀 '" + prefix + "' 的文件");
                        try {
                            List<FileDto> allFiles = qiniuService.listFiles("", MAX_FILES);
                            if (!allFiles.isEmpty()) {
                                model.addAttribute("info", "已显示所有文件");
                                model.addAttribute("prefix", "");
                                files = allFiles;
                            }
                        } catch (Exception ex) { 
                            log.warn("尝试获取所有文件失败", ex);
                        }
                    } else {
                        model.addAttribute("info", "存储空间中没有文件");
                    }
                } else if (files.size() >= MAX_FILES) {
                    // 文件数量达到上限时显示警告
                    model.addAttribute("warning", "由于数量限制，只显示前" + MAX_FILES + "个文件");
                }
            } catch (QiniuException e) {
                String msg = getQiniuErrorMessage(e);
                log.error("获取文件列表失败: 代码={}, 消息={}", e.code(), e.getMessage());
                model.addAttribute("error", msg);
            }
            
            // 添加数据到模型
            model.addAttribute("files", files);
            model.addAttribute("prefix", prefix);
        } catch (Exception e) {
            log.error("请求处理错误", e);
            addError(model, e.getMessage());
        }
        return "index";
    }

    /**
     * 向模型中添加错误信息
     * 
     * @param model 视图模型
     * @param message 错误消息
     */
    private void addError(Model model, String message) {
        model.addAttribute("error", message);
        model.addAttribute("files", new ArrayList<>());
    }

    /**
     * 根据七牛云异常代码获取友好的错误消息
     * 
     * @param e 七牛云异常
     * @return 友好的错误消息
     */
    private String getQiniuErrorMessage(QiniuException e) {
        switch (e.code()) {
            case 401:
            case 403: return "认证或权限错误，请检查七牛云配置";
            case 612:
            case 631: return "存储空间不可用";
            case 298: return "七牛云服务响应超时";
            default: return "获取文件列表失败: " + e.getMessage();
        }
    }

    /**
     * 处理文件上传请求
     * 
     * @param file 上传的文件对象
     * @return 包含上传结果的JSON响应
     */
    @PostMapping("/upload")
    @ResponseBody
    public ResponseEntity<Map<String, Object>> upload(@RequestParam("file") MultipartFile file) {
        if (file.isEmpty()) {
            return error("上传文件不能为空");
        }
        
        try {
            String fileName = file.getOriginalFilename();
            if (fileName == null || fileName.isEmpty()) {
                return error("文件名无效");
            }
            
            // 执行文件上传到七牛云
            String url = qiniuService.uploadFile(file, fileName);
            if (url != null) {
                log.info("文件上传成功: {}", fileName);
                return success(Map.of("url", url, "fileName", fileName));
            } else {
                return error("上传失败，七牛云服务返回错误");
            }
        } catch (Exception e) {
            log.error("上传失败", e);
            return error("上传失败: " + e.getMessage());
        }
    }

    /**
     * 处理文件删除请求
     * 
     * @param fileName 要删除的文件名
     * @return 包含删除结果的JSON响应
     */
    @DeleteMapping("/delete")
    @ResponseBody
    public ResponseEntity<Map<String, Object>> delete(@RequestParam("fileName") String fileName) {
        try {
            boolean success = qiniuService.deleteFile(fileName);
            return success ? success(null) : error("删除文件失败");
        } catch (Exception e) {
            log.error("删除失败", e);
            return error("删除失败: " + e.getMessage());
        }
    }

    /**
     * 获取文件下载链接并直接下载
     * 
     * @param fileName 要下载的文件名
     * @param response HTTP响应对象
     */
    @GetMapping("/download")
    public void download(@RequestParam("fileName") String fileName, HttpServletResponse response) {
        log.info("请求下载文件: {}", fileName);
        
        try {
            // 获取文件信息
            FileDto fileInfo = qiniuService.getFileInfo(fileName);
            if (fileInfo == null) {
                log.error("文件不存在: {}", fileName);
                response.sendError(HttpServletResponse.SC_NOT_FOUND, "文件不存在");
                return;
            }
            
            // 获取文件字节数组
            byte[] fileData = qiniuService.getFileBytes(fileName);
            if (fileData == null) {
                log.error("获取文件内容失败");
                response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "获取文件内容失败");
                return;
            }
            
            // 设置响应头
            response.setContentType(fileInfo.getMimeType() != null ? fileInfo.getMimeType() : "application/octet-stream");
            response.setHeader("Content-Disposition", "attachment; filename=\"" + 
                URLEncoder.encode(fileName, "UTF-8").replace("+", "%20") + "\"");
            response.setContentLength(fileData.length);
            
            // 写入文件内容
            response.getOutputStream().write(fileData);
            response.getOutputStream().flush();
            
        } catch (Exception e) {
            log.error("文件下载失败: {}, 错误: {}", fileName, e.getMessage(), e);
            try {
                response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "文件下载失败");
            } catch (IOException ex) {
                log.error("发送错误响应失败", ex);
            }
        }
    }
    
    /**
     * 获取文件详细信息
     * 
     * @param fileName 文件名
     * @return 包含文件详细信息的JSON响应
     */
    @GetMapping("/api/file/details")
    @ResponseBody
    public ResponseEntity<Map<String, Object>> getFileDetails(@RequestParam("fileName") String fileName) {
        log.info("获取文件详情: {}", fileName);
        
        if (fileName == null || fileName.isEmpty()) {
            return error("文件名不能为空");
        }
        
        try {
            // 获取文件信息
            FileDto fileInfo = qiniuService.getFileInfo(fileName);
            if (fileInfo == null) {
                return error("文件不存在");
            }
            
            // 生成文件URL
            String url = qiniuService.generateShareLink(fileName, 1); // 1小时有效期
            
            // 构建响应数据
            Map<String, Object> details = new HashMap<>();
            details.put("fileName", fileInfo.getKey());
            details.put("mimeType", fileInfo.getMimeType());
            details.put("size", fileInfo.getSize());
            details.put("putTime", fileInfo.getPutTime());
            details.put("storageType", fileInfo.getStorageType());
            details.put("etag", fileInfo.getEtag());
            details.put("url", url);
            
            return success(details);
        } catch (Exception e) {
            log.error("获取文件详情失败", e);
            return error("获取文件详情失败: " + e.getMessage());
        }
    }
    
    /**
     * API接口：获取文件列表
     * 供前端AJAX调用或外部系统集成
     * 
     * @param prefix 可选的文件名前缀过滤条件
     * @return 包含文件列表的JSON响应
     */
    @GetMapping("/api/files")
    @ResponseBody
    public ResponseEntity<Map<String, Object>> getFiles(@RequestParam(value = "prefix", required = false, defaultValue = "") String prefix) {
        try {
            if (!qiniuService.validateConfig()) {
                return error("七牛云配置无效，请检查配置信息", HttpStatus.BAD_REQUEST);
            }
            
            List<FileDto> files = qiniuService.listFiles(prefix, MAX_FILES);
            Map<String, Object> result = new HashMap<>();
            result.put("files", files);
            
            if (files.size() >= MAX_FILES) {
                result.put("message", "由于数量限制，只返回前" + MAX_FILES + "个文件");
            }
            
            return success(result);
        } catch (Exception e) {
            log.error("获取文件列表失败", e);
            return error("获取文件列表失败: " + e.getMessage());
        }
    }
    
    /**
     * API接口：获取文件列表（支持后端排序和过滤）
     * 在服务器端执行文件排序和过滤，减轻前端负担
     * 
     * @param prefix 文件名前缀
     * @param search 搜索关键词
     * @param sortBy 排序字段 (name, time, size)
     * @param order 排序顺序 (asc, desc)
     * @return 排序和过滤后的文件列表
     */
    @GetMapping("/api/files/advanced")
    @ResponseBody
    public ResponseEntity<Map<String, Object>> getAdvancedFiles(
            @RequestParam(value = "prefix", required = false, defaultValue = "") String prefix,
            @RequestParam(value = "search", required = false) String search,
            @RequestParam(value = "sortBy", required = false, defaultValue = "time") String sortBy,
            @RequestParam(value = "order", required = false, defaultValue = "desc") String order) {
        
        try {
            if (!qiniuService.validateConfig()) {
                return error("七牛云配置无效，请检查配置信息", HttpStatus.BAD_REQUEST);
            }
            
            // 获取文件列表
            List<FileDto> files = qiniuService.listFiles(prefix, MAX_FILES);
            
            // 应用搜索过滤
            if (search != null && !search.isEmpty()) {
                String searchLower = search.toLowerCase();
                files = files.stream()
                        .filter(file -> 
                            (file.getKey() != null && file.getKey().toLowerCase().contains(searchLower)) || 
                            (file.getMimeType() != null && file.getMimeType().toLowerCase().contains(searchLower)))
                        .collect(Collectors.toList());
            }
            
            // 应用排序
            switch (sortBy) {
                case "name":
                    files = files.stream()
                            .sorted("asc".equals(order) 
                                    ? Comparator.comparing(FileDto::getKey, Comparator.nullsLast(String::compareTo))
                                    : Comparator.comparing(FileDto::getKey, Comparator.nullsLast(String::compareTo)).reversed())
                            .collect(Collectors.toList());
                    break;
                case "size":
                    files = files.stream()
                            .sorted("asc".equals(order) 
                                    ? Comparator.comparingLong(FileDto::getSize)
                                    : Comparator.comparingLong(FileDto::getSize).reversed())
                            .collect(Collectors.toList());
                    break;
                case "time":
                default:
                    files = files.stream()
                            .sorted("asc".equals(order) 
                                    ? Comparator.comparingLong(FileDto::getPutTime)
                                    : Comparator.comparingLong(FileDto::getPutTime).reversed())
                            .collect(Collectors.toList());
            }
            
            // 构建响应
            Map<String, Object> result = new HashMap<>();
            result.put("files", files);
            result.put("totalCount", files.size());
            
            return success(result);
        } catch (Exception e) {
            log.error("高级文件列表获取失败", e);
            return error("获取文件列表失败: " + e.getMessage());
        }
    }
    
    /**
     * 处理文件重命名请求
     * 
     * @param oldFileName 原文件名
     * @param newFileName 新文件名
     * @return 包含重命名结果的JSON响应
     */
    @PostMapping("/rename")
    @ResponseBody
    public ResponseEntity<Map<String, Object>> rename(
            @RequestParam("oldFileName") String oldFileName,
            @RequestParam("newFileName") String newFileName) {
        
        log.info("重命名文件请求: {} -> {}", oldFileName, newFileName);
        
        if (oldFileName == null || oldFileName.isEmpty()) {
            return error("原文件名不能为空");
        }
        
        if (newFileName == null || newFileName.isEmpty()) {
            return error("新文件名不能为空");
        }
        
        try {
            // 调用服务执行重命名
            boolean success = qiniuService.renameFile(oldFileName, newFileName);
            if (success) {
                return success(Map.of("oldFileName", oldFileName, "newFileName", newFileName));
            } else {
                return error("重命名失败");
            }
        } catch (Exception e) {
            log.error("重命名文件失败", e);
            return error("重命名失败: " + e.getMessage());
        }
    }
    
    /**
     * 处理文件共享请求
     * 
     * @param fileName 文件名
     * @param expirationHours 有效期（小时）
     * @return 包含共享链接的JSON响应
     */
    @PostMapping("/share")
    @ResponseBody
    public ResponseEntity<Map<String, Object>> share(
            @RequestParam("fileName") String fileName,
            @RequestParam("expirationHours") int expirationHours) {
        
        log.info("生成共享链接请求: 文件={}, 有效期={}小时", fileName, expirationHours);
        
        if (fileName == null || fileName.isEmpty()) {
            return error("文件名不能为空");
        }
        
        try {
            // 检查文件是否存在
            FileDto fileInfo = qiniuService.getFileInfo(fileName);
            if (fileInfo == null) {
                return error("文件不存在");
            }
            
            // 生成共享链接
            String shareUrl = qiniuService.generateShareLink(fileName, expirationHours);
            
            if (shareUrl != null) {
                return success(Map.of(
                    "shareUrl", shareUrl,
                    "expirationHours", expirationHours
                ));
            } else {
                return error("生成共享链接失败");
            }
        } catch (Exception e) {
            log.error("生成共享链接失败", e);
            return error("生成共享链接失败: " + e.getMessage());
        }
    }
    
    /**
     * 创建成功响应
     * 
     * @param data 响应数据
     * @return HTTP 200响应，包含success标志和数据
     */
    private ResponseEntity<Map<String, Object>> success(Map<String, Object> data) {
        Map<String, Object> result = new HashMap<>();
        result.put("success", true);
        if (data != null) {
            result.putAll(data);
        }
        return ResponseEntity.ok(result);
    }
    
    /**
     * 创建错误响应，使用默认的400状态码
     * 
     * @param message 错误消息
     * @return HTTP 400响应，包含错误信息
     */
    private ResponseEntity<Map<String, Object>> error(String message) {
        return error(message, HttpStatus.BAD_REQUEST);
    }
    
    /**
     * 创建自定义状态码的错误响应
     * 
     * @param message 错误消息
     * @param status HTTP状态码
     * @return 指定状态码的HTTP响应，包含错误信息
     */
    private ResponseEntity<Map<String, Object>> error(String message, HttpStatus status) {
        Map<String, Object> result = new HashMap<>();
        result.put("success", false);
        result.put("message", message);
        return ResponseEntity.status(status).body(result);
    }
    
    /**
     * 全局异常处理
     * 捕获控制器中未处理的所有异常
     * 
     * @param e 捕获到的异常
     * @return 包含错误信息的JSON响应
     */
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public ResponseEntity<Map<String, Object>> handleException(Exception e) {
        log.error("全局异常", e);
        return error("系统错误: " + e.getMessage());
    }

    @GetMapping("/api/file/share")
    @ResponseBody
    public Map<String, Object> generateShareLink(@RequestParam String fileName) {
        Map<String, Object> result = new HashMap<>();
        
        if (StringUtils.isEmpty(fileName)) {
            result.put("success", false);
            result.put("message", "文件名不能为空");
            return result;
        }

        try {
            // 默认生成24小时有效的分享链接
            String shareUrl = qiniuService.generateShareLink(fileName, 24);
            result.put("success", true);
            result.put("url", shareUrl);
        } catch (Exception e) {
            result.put("success", false);
            result.put("message", "生成分享链接失败：" + e.getMessage());
        }
        
        return result;
    }
} 